package info.pinlab.pinplayer.view.swing;


import info.pinlab.pinplayer.control.SessionController;
import info.pinlab.pinplayer.model.response.Response;
import info.pinlab.pinplayer.model.task.Task;
import info.pinlab.pinplayer.view.NavigatorView;
import info.pinlab.pinplayer.view.PlayerTopView;
import info.pinlab.pinplayer.view.TaskView;

import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.WindowListener;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;

import org.apache.log4j.Logger;

public class PlayerTopPanel implements PlayerTopView, WindowListener, WindowFocusListener{
	public static Logger logger = Logger.getLogger(PlayerTopPanel.class);

	private final JFrame frame;
	private final Container contentPane;

	private final TopNavigationPanel naviPanel ;
	private final GridBagConstraints dispPanelGBC;
	
	private SessionController control = null;
	
	class MouseClickListListener extends MouseAdapter{
		@Override
		public void mousePressed(MouseEvent e){
			switch (e.getButton()) {
			case MouseEvent.BUTTON1:
//				System.out.println("1 " + e);
				break;
			case MouseEvent.BUTTON2:
//				System.out.println("2 " + e);
				break;
			case MouseEvent.BUTTON3: //-- RIGHT CLICK
				PopUpMenu pop = new PopUpMenu();
				pop.show(e.getComponent(), e.getX(), e.getY());
				break;
			default:
				break;
			}
	    }
	}
	

	
	@SuppressWarnings("serial")
	private class PopUpMenu extends JPopupMenu implements ActionListener{
		final JMenuItem sndMenu;
		final JMenuItem fontMenu;
		final JMenuItem remoteMenu;
		
		final JDialog sndDialog; 
		
		PopUpMenu(){
			super();
			sndMenu = new JMenuItem("Sound settings");
			fontMenu = new JMenuItem("Font settings");
			remoteMenu = new JMenuItem("Remote settings");

			add(sndMenu);
			add(remoteMenu);
			add(fontMenu);

			sndMenu.addActionListener(this);
			fontMenu.addActionListener(this);
			remoteMenu.addActionListener(this);

			sndDialog = new JDialog(PlayerTopPanel.this.frame, "Sound settings", true);
			//TODO: audio
//			sndDialog.getContentPane().add(audioDevicePanel);
//			sndDialog.setSize(150,450);
		}
		@Override
		public void actionPerformed(ActionEvent e){
			
			if(sndMenu.equals(e.getSource())){
				Point cursor = MouseInfo.getPointerInfo().getLocation();
				sndDialog.setBounds(cursor.x, cursor.y, 120, 550);
				sndDialog.setVisible(true);
				return;
			}
		}
	}
	
	static public void setNimbusLF(){
		try{
			for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
				if ("Nimbus".equals(info.getName())) {
					logger.info("Look and feel is set to " + info.getName());
					UIManager.setLookAndFeel(info.getClassName());
					break;
				}
			}
		} catch (Exception e) {
			logger.error("Failed to set look and feel to Nimbus!");
		}
	}
	
	

	
	private class InitGuiComonents{
		private JFrame frame_;
		private Container contentPane_;

		private TopNavigationPanel naviPanel_ ;
//		private JPanel displayPanel_;
		private GridBagConstraints dispPanelGBC_;
//		private JPanel responsePanel_;

		private InitGuiComonents() throws InterruptedException, InvocationTargetException{
			SwingUtilities.invokeAndWait(new Runnable() {
				@Override
				public void run() {
					frame_ = new JFrame();
					frame_.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
					contentPane_ =  frame_.getContentPane();
					contentPane_.setLayout(new GridBagLayout());

					frame_.addWindowListener(PlayerTopPanel.this);
					frame_.addMouseListener(new MouseClickListListener());
					frame_.addWindowFocusListener(PlayerTopPanel.this);

					naviPanel_ = new TopNavigationPanel();
					GridBagConstraints gbc = GbcFactory.getRow();
					gbc.gridx = 0;
					gbc.gridy = 0;
					contentPane_.add(naviPanel_.getPanel(), gbc);

//					displayPanel_ = new JPanel();
					dispPanelGBC_ = GbcFactory.getFillBoth();
					dispPanelGBC_.gridy = 1;
//					contentPane_.add(displayPanel_, dispPanelGBC_);

//					responsePanel_ = new JPanel();
//					responsePanelGBC_ = GbcFactory.getRow();
//					responsePanelGBC_.gridy = 2;
//					contentPane_.add(responsePanel_, responsePanelGBC_);
				}
			});
		}
	}
	
	
	
	public PlayerTopPanel(){
		//-- set nimbus on a non Swing thread 
		setNimbusLF();

		//-- start gui on Swing thread! 
		InitGuiComonents comp = null;
		try {
			comp = new InitGuiComonents();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
		this.frame = comp.frame_;
		this.contentPane = comp.contentPane_;
		this.naviPanel = comp.naviPanel_;
		this.dispPanelGBC = comp.dispPanelGBC_;
		
	}

	
	
	@Override
	public void setLabel(String label) {
		frame.setTitle(label);
	}
	

	@Override
	public void startGui(){
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				if(control==null){
					frame.setTitle("No control object!");
				}
				frame.setSize(500,450);
				
				
				
				frame.setVisible(true);
			}
		});
	}


	/**
	 * Clear everything except for navigation
	 */
	private void clearView(){
		for(Component comp : contentPane.getComponents()){
			if(!comp.equals(naviPanel)){
				contentPane.remove(comp);
			}
		}
	}
	
	@Override
	public void setTaskView(final TaskView view){
		if(!(view instanceof TaskViewPanel)){
//			logger.error("Not a TaskViewPanel in setter! '" + view.getClass() + "' can't be set for panel!");
			throw new IllegalArgumentException("Not a TaskViewPanel in setter! '" + view.getClass() + "' can't be set for panel!");
		}
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				clearView();
				contentPane.add(((TaskViewPanel)view).getPanel(), dispPanelGBC);
				contentPane.validate();
				contentPane.repaint();
			}
		});
	}
	
	
	
	
	private static class ViewSetter implements Runnable{
		TaskView view;
		final Task task;
		Class<?> panelClass = null;
		
		ViewSetter(Task task){
			this.task = task;
			String panelName = this.getClass().getPackage().getName()
					+ "." 
					+task.getClass().getSimpleName() + "Panel";
			try {
				//-- dynamically create task view
				panelClass = Class.forName(panelName);
			} catch (ClassNotFoundException e) {
				logger.error("No such class available as '"+ panelClass +"'");
				e.printStackTrace();
			}
		}
		
		public void run() {
			logger.debug("Dynamically creating view: '" + panelClass +"'");
			try {
				view = (TaskView) panelClass.newInstance();
			} catch (InstantiationException e) {
				logger.error("Can't instantiate class '"+ panelClass +"'");
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			//-- after getting view instance
			if(view!=null){
				view.setTask(task);
			}else{
				throw new IllegalStateException("No view for task '" + task.getClass()+"'");
			}
			
		}
	}
	
	@Override
	public TaskView setTaskView(final Task task){
		ViewSetter vs = new ViewSetter(task);
		if(SwingUtilities.isEventDispatchThread()){
			vs.run();
		}else{
			try {
				SwingUtilities.invokeAndWait(vs);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		setTaskView(vs.view);
		return vs.view;
	}

	
	@Override
	public void windowClosing(WindowEvent e){
		if(control!=null){
			new Thread(new Runnable() {
				@Override
				public void run() {
					control.reqCloseWindow();
				}
			}).start();
		}else{
			logger.warn("No session controller is set!");
			if(showCloseConfirmDialog()){
				this.dispose();
			}
		}
	}
	
	
	@Override
	public void setResponse(Response resp) {	}
	@Override
	public void windowOpened(WindowEvent e) {	}
	@Override
	public void windowClosed(WindowEvent e) {	}
	@Override
	public void windowIconified(WindowEvent e) {	}
	@Override
	public void windowDeiconified(WindowEvent e) {	}
	@Override
	public void windowActivated(WindowEvent e) {	}
	@Override
	public void windowDeactivated(WindowEvent e) {	}

	@Override
	public void setSessionController(SessionController session) {
//		if(naviPanel!=null)
//			naviPanel.setStepController(session);
		if(session!=null){
			control = session;
			session.setTopView(this);
		}
	}


	@Override
	public NavigatorView getNaviView() {
		return naviPanel;
	}


	@Override
	public boolean showCloseConfirmDialog() {
		int quit = JOptionPane.showOptionDialog(
				frame, "Do you want to quit?", "" +
						"Exiting program..", 
						JOptionPane.YES_NO_OPTION, 
						JOptionPane.QUESTION_MESSAGE, 
						null, new String[]{"yes", "no"}, 1); 
		if(quit==0)
			return true;
		return false;
	}


	@Override
	public void dispose(){
		SwingUtilities.invokeLater(new Runnable() {
				@Override
				public void run() {
					frame.dispose();
				}
			});
	}


	@Override
	public int showAudioDevSelector(List<String> deviceNames){
		//TODO: implement this!
		return -1;
	}

	
	@Override
	public void windowGainedFocus(WindowEvent arg0){
//		Container parent = frame;
//			for(Component comp : parent.getComponents()){
//				System.out.println(comp);
//			}
//		System.out.println(" GOT FOCUS ! " + SwingUtilities.isEventDispatchThread());
//		traverseComps(frame.getContentPane());
	}

	
//	private void traverseComps(Container cont){
//		if(cont==null){
//			return;
//		}
//		for(Component comp : cont.getComponents()){
//			System.out.println("  " + comp);
//			if(comp instanceof Container){
//				traverseComps((Container)comp);
//			}
//		}
//		
//	}
	


	@Override
	public void windowLostFocus(WindowEvent arg0) {
	}

}
